<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SumerCore DevOps - WebSocket 测试工具</title>
    <script src="https://cdn.jsdelivr.net/npm/@microsoft/signalr@8.0.0/dist/browser/signalr.min.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            padding: 20px;
        }

        .container {
            max-width: 1200px;
            margin: 0 auto;
            background: white;
            border-radius: 12px;
            box-shadow: 0 20px 60px rgba(0,0,0,0.3);
            overflow: hidden;
        }

        .header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 30px;
            text-align: center;
        }

        .header h1 {
            font-size: 28px;
            margin-bottom: 10px;
        }

        .header p {
            opacity: 0.9;
        }

        .content {
            padding: 30px;
        }

        .section {
            margin-bottom: 30px;
            border: 1px solid #e0e0e0;
            border-radius: 8px;
            padding: 20px;
        }

        .section h2 {
            color: #667eea;
            margin-bottom: 15px;
            font-size: 20px;
        }

        .form-group {
            margin-bottom: 15px;
        }

        label {
            display: block;
            margin-bottom: 5px;
            color: #555;
            font-weight: 500;
        }

        input[type="text"],
        input[type="password"],
        input[type="number"] {
            width: 100%;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-size: 14px;
        }

        button {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: none;
            padding: 12px 24px;
            border-radius: 6px;
            cursor: pointer;
            font-size: 14px;
            font-weight: 600;
            transition: transform 0.2s, box-shadow 0.2s;
        }

        button:hover {
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
        }

        button:active {
            transform: translateY(0);
        }

        button:disabled {
            opacity: 0.5;
            cursor: not-allowed;
            transform: none;
        }

        .status {
            padding: 12px;
            border-radius: 6px;
            margin: 15px 0;
            font-weight: 500;
        }

        .status.connected {
            background: #d4edda;
            color: #155724;
            border: 1px solid #c3e6cb;
        }

        .status.disconnected {
            background: #f8d7da;
            color: #721c24;
            border: 1px solid #f5c6cb;
        }

        .status.info {
            background: #d1ecf1;
            color: #0c5460;
            border: 1px solid #bee5eb;
        }

        .log-container {
            background: #1e1e1e;
            color: #d4d4d4;
            font-family: 'Consolas', 'Monaco', monospace;
            font-size: 13px;
            padding: 15px;
            border-radius: 6px;
            height: 400px;
            overflow-y: auto;
            line-height: 1.6;
        }

        .log-entry {
            margin-bottom: 5px;
            padding: 5px;
            border-radius: 3px;
        }

        .log-entry.info {
            color: #4fc3f7;
        }

        .log-entry.success {
            color: #81c784;
        }

        .log-entry.error {
            color: #e57373;
        }

        .log-entry.warning {
            color: #ffb74d;
        }

        .log-entry .timestamp {
            color: #888;
            margin-right: 8px;
        }

        .button-group {
            display: flex;
            gap: 10px;
            flex-wrap: wrap;
        }

        .grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
            gap: 20px;
        }

        .clear-btn {
            background: #e57373;
            margin-left: 10px;
        }

        .clear-btn:hover {
            box-shadow: 0 5px 15px rgba(229, 115, 115, 0.4);
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>🚀 SumerCore DevOps WebSocket 测试工具</h1>
            <p>测试SignalR实时通信功能</p>
        </div>

        <div class="content">
            <!-- 登录区域 -->
            <div class="section">
                <h2>🔐 1. 登录获取Token</h2>
                <div class="grid">
                    <div>
                        <div class="form-group">
                            <label>API地址:</label>
                            <input type="text" id="apiUrl" value="http://localhost:5000">
                        </div>
                        <div class="form-group">
                            <label>用户名:</label>
                            <input type="text" id="username" value="admin">
                        </div>
                        <div class="form-group">
                            <label>密码:</label>
                            <input type="password" id="password" value="Admin@123456">
                        </div>
                        <div class="button-group">
                            <button onclick="login()">登录</button>
                        </div>
                    </div>
                    <div>
                        <div class="form-group">
                            <label>Access Token:</label>
                            <input type="text" id="token" readonly placeholder="登录后自动填充">
                        </div>
                        <div id="loginStatus" class="status disconnected" style="display: none;">
                            未登录
                        </div>
                    </div>
                </div>
            </div>

            <!-- WebSocket连接区域 -->
            <div class="section">
                <h2>🔌 2. WebSocket 连接</h2>
                <div class="button-group">
                    <button onclick="connectWebSocket()" id="connectBtn">连接 WebSocket</button>
                    <button onclick="disconnectWebSocket()" id="disconnectBtn" disabled>断开连接</button>
                </div>
                <div id="connectionStatus" class="status disconnected">
                    未连接
                </div>
            </div>

            <!-- 订阅区域 -->
            <div class="section">
                <h2>📡 3. 订阅事件</h2>
                <div class="grid">
                    <div>
                        <h3 style="margin-bottom: 10px;">任务日志</h3>
                        <div class="form-group">
                            <label>Task ID:</label>
                            <input type="number" id="taskId" value="1">
                        </div>
                        <div class="button-group">
                            <button onclick="subscribeTaskLogs()">订阅任务日志</button>
                            <button onclick="unsubscribeTaskLogs()">取消订阅</button>
                        </div>
                    </div>
                    <div>
                        <h3 style="margin-bottom: 10px;">任务状态</h3>
                        <div class="form-group">
                            <label>Pipeline Instance ID:</label>
                            <input type="number" id="instanceId" value="1">
                        </div>
                        <div class="button-group">
                            <button onclick="subscribeTaskStatus()">订阅任务状态</button>
                            <button onclick="unsubscribeTaskStatus()">取消订阅</button>
                        </div>
                    </div>
                </div>
                <div style="margin-top: 20px;">
                    <h3 style="margin-bottom: 10px;">节点状态</h3>
                    <div class="button-group">
                        <button onclick="subscribeNodeStatus()">订阅节点状态</button>
                        <button onclick="unsubscribeNodeStatus()">取消订阅</button>
                    </div>
                </div>
            </div>

            <!-- 日志显示区域 -->
            <div class="section">
                <h2>📝 4. 实时日志
                    <button class="clear-btn" onclick="clearLogs()">清空日志</button>
                </h2>
                <div class="log-container" id="logContainer">
                    <div class="log-entry info">等待连接...</div>
                </div>
            </div>
        </div>
    </div>

    <script>
        let connection = null;
        let accessToken = null;

        // 添加日志
        function addLog(message, type = 'info') {
            const logContainer = document.getElementById('logContainer');
            const timestamp = new Date().toLocaleTimeString();
            const logEntry = document.createElement('div');
            logEntry.className = `log-entry ${type}`;
            logEntry.innerHTML = `<span class="timestamp">[${timestamp}]</span>${message}`;
            logContainer.appendChild(logEntry);
            logContainer.scrollTop = logContainer.scrollHeight;
        }

        // 清空日志
        function clearLogs() {
            document.getElementById('logContainer').innerHTML = '';
            addLog('日志已清空', 'info');
        }

        // 登录
        async function login() {
            const apiUrl = document.getElementById('apiUrl').value;
            const username = document.getElementById('username').value;
            const password = document.getElementById('password').value;

            try {
                addLog(`正在登录: ${username}...`, 'info');
                const response = await fetch(`${apiUrl}/api/Auth/login`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({ username, password })
                });

                if (!response.ok) {
                    const error = await response.json();
                    throw new Error(error.errorMessage || '登录失败');
                }

                const result = await response.json();
                accessToken = result.data.accessToken;
                document.getElementById('token').value = accessToken;

                const loginStatus = document.getElementById('loginStatus');
                loginStatus.className = 'status connected';
                loginStatus.textContent = `✅ 已登录: ${result.data.user.displayName}`;
                loginStatus.style.display = 'block';

                addLog(`登录成功! 用户: ${result.data.user.displayName}`, 'success');
            } catch (error) {
                addLog(`登录失败: ${error.message}`, 'error');
                const loginStatus = document.getElementById('loginStatus');
                loginStatus.className = 'status disconnected';
                loginStatus.textContent = `❌ 登录失败: ${error.message}`;
                loginStatus.style.display = 'block';
            }
        }

        // 连接WebSocket
        async function connectWebSocket() {
            if (!accessToken) {
                alert('请先登录获取Token！');
                return;
            }

            try {
                const apiUrl = document.getElementById('apiUrl').value;
                const hubUrl = `${apiUrl}/hubs/devops`;

                addLog(`正在连接到: ${hubUrl}`, 'info');

                connection = new signalR.HubConnectionBuilder()
                    .withUrl(hubUrl, {
                        accessTokenFactory: () => accessToken
                    })
                    .withAutomaticReconnect()
                    .configureLogging(signalR.LogLevel.Information)
                    .build();

                // 注册事件监听器
                connection.on('ReceiveTaskLog', (data) => {
                    addLog(`📋 [TaskLog] Task ${data.taskId}: [${data.logLevel}] ${data.logContent}`, 'info');
                });

                connection.on('ReceiveTaskStatus', (data) => {
                    addLog(`📊 [TaskStatus] Task ${data.taskId}: ${data.status} (${data.progress}%)`, 'success');
                });

                connection.on('ReceiveNodeStatus', (data) => {
                    const status = data.isOnline ? '🟢 在线' : '🔴 离线';
                    addLog(`🖥️ [NodeStatus] ${data.nodeName} ${status} - CPU: ${data.cpuUsage}%, Mem: ${data.memoryUsage}%`, 'warning');
                });

                connection.onreconnecting(() => {
                    addLog('正在重新连接...', 'warning');
                    updateConnectionStatus('重连中...', 'info');
                });

                connection.onreconnected(() => {
                    addLog('重新连接成功!', 'success');
                    updateConnectionStatus('✅ 已连接', 'connected');
                });

                connection.onclose(() => {
                    addLog('连接已关闭', 'error');
                    updateConnectionStatus('❌ 未连接', 'disconnected');
                    document.getElementById('connectBtn').disabled = false;
                    document.getElementById('disconnectBtn').disabled = true;
                });

                await connection.start();
                addLog('WebSocket连接成功!', 'success');
                updateConnectionStatus('✅ 已连接', 'connected');
                document.getElementById('connectBtn').disabled = true;
                document.getElementById('disconnectBtn').disabled = false;

            } catch (error) {
                addLog(`连接失败: ${error.message}`, 'error');
                updateConnectionStatus('❌ 连接失败', 'disconnected');
            }
        }

        // 断开WebSocket
        async function disconnectWebSocket() {
            if (connection) {
                await connection.stop();
                connection = null;
                addLog('已断开WebSocket连接', 'info');
            }
        }

        // 更新连接状态
        function updateConnectionStatus(text, className) {
            const status = document.getElementById('connectionStatus');
            status.textContent = text;
            status.className = `status ${className}`;
        }

        // 订阅任务日志
        async function subscribeTaskLogs() {
            if (!connection) {
                alert('请先连接WebSocket!');
                return;
            }
            const taskId = parseInt(document.getElementById('taskId').value);
            await connection.invoke('SubscribeTaskLogs', taskId);
            addLog(`✅ 已订阅任务日志: Task ${taskId}`, 'success');
        }

        // 取消订阅任务日志
        async function unsubscribeTaskLogs() {
            if (!connection) return;
            const taskId = parseInt(document.getElementById('taskId').value);
            await connection.invoke('UnsubscribeTaskLogs', taskId);
            addLog(`❌ 已取消订阅任务日志: Task ${taskId}`, 'info');
        }

        // 订阅任务状态
        async function subscribeTaskStatus() {
            if (!connection) {
                alert('请先连接WebSocket!');
                return;
            }
            const instanceId = parseInt(document.getElementById('instanceId').value);
            await connection.invoke('SubscribeTaskStatus', instanceId);
            addLog(`✅ 已订阅任务状态: Instance ${instanceId}`, 'success');
        }

        // 取消订阅任务状态
        async function unsubscribeTaskStatus() {
            if (!connection) return;
            const instanceId = parseInt(document.getElementById('instanceId').value);
            await connection.invoke('UnsubscribeTaskStatus', instanceId);
            addLog(`❌ 已取消订阅任务状态: Instance ${instanceId}`, 'info');
        }

        // 订阅节点状态
        async function subscribeNodeStatus() {
            if (!connection) {
                alert('请先连接WebSocket!');
                return;
            }
            await connection.invoke('SubscribeNodeStatus');
            addLog(`✅ 已订阅节点状态`, 'success');
        }

        // 取消订阅节点状态
        async function unsubscribeNodeStatus() {
            if (!connection) return;
            await connection.invoke('UnsubscribeNodeStatus');
            addLog(`❌ 已取消订阅节点状态`, 'info');
        }

        // 页面卸载时清理连接
        window.addEventListener('beforeunload', () => {
            if (connection) {
                connection.stop();
            }
        });
    </script>
</body>
</html>
